package com.jsftoolkit.gen.builder;

import javax.faces.component.UIComponent;
import javax.faces.event.FacesListener;

import com.jsftoolkit.gen.info.ComponentInfo;
import com.jsftoolkit.gen.info.ConstantInfo;
import com.jsftoolkit.gen.info.PropertyInfo;
import com.jsftoolkit.utils.Utils;

/**
 * Utility class for creating {@link ComponentInfo} instances concisely.
 * 
 * @author noah
 * @see ClassBuilder
 * 
 */
public class ComponentBuilder extends ClassBuilder<ComponentBuilder> {

	private final ComponentInfo info;

	/**
	 * Creates a new {@link ComponentInfo} instance to operate on.
	 */
	public ComponentBuilder() {
		this(new ComponentInfo());
	}

	/**
	 * 
	 * @param info
	 *            the {@link ComponentInfo} instance to operate on.
	 */
	public ComponentBuilder(ComponentInfo info) {
		super(info);
		this.info = (ComponentInfo) super.getInfo();
	}

	/**
	 * Specifies the class the generated component code should extend.
	 * 
	 * @param superClass
	 *            the component super class
	 * @return this
	 */
	public ComponentBuilder extending(Class<? extends UIComponent> superClass) {
		info.setSuperClass(superClass);
		return this;
	}

	/**
	 * 
	 * @param componentType
	 *            the COMPONENT_TYPE identifier
	 * @return this
	 */
	public ComponentBuilder ofType(String componentType) {
		info.setType(componentType);
		return this;
	}

	/**
	 * 
	 * @param componentFamily
	 *            the COMPONENT_FAMILY identifier
	 * @return this
	 */
	public ComponentBuilder inFamily(String componentFamily) {
		info.setFamily(componentFamily);
		return this;
	}

	/**
	 * Refer to {@link PropertyInfo}.
	 * 
	 * @param constantName
	 *            the name of the constant (e.g. "FOO") that will be created in
	 *            the component class, whose value will be the attribute name
	 *            corresponding to this property (e.g. "foo")
	 * @param info
	 *            the {@link PropertyInfo} instance describing the property.
	 * @return this
	 */
	public ComponentBuilder andProperty(String constantName, PropertyInfo info) {
		this.info.getProperties().put(constantName, info);
		return this;
	}

	/**
	 * Adds multiple properties at once. The constant name will be obtained by
	 * taking {@link PropertyInfo#getName()} and passing it to
	 * {@link Utils#toConstantName(CharSequence)}.
	 * 
	 * @param properties
	 *            the properties to add
	 * @return this
	 */
	public ComponentBuilder andProperties(PropertyInfo... properties) {
		for (PropertyInfo info : properties) {
			this.info.addProperty(info);
		}
		return this;
	}

	/**
	 * Adds additional constants. See {@link ConstantInfo}.
	 * 
	 * @param constants
	 *            the constants to add
	 * @return this
	 */
	public ComponentBuilder andConstants(ConstantInfo... constants) {
		for (ConstantInfo info : constants) {
			this.info.addConstant(info);
		}
		return this;
	}

	/**
	 * Access the configuration information.
	 * 
	 * @see ConfigBuilder
	 * 
	 * @return a {@link ConfigBuilder}
	 */
	public ConfigBuilder withConfig() {
		return new ConfigBuilder(info.getConfig()).forComponent(info);
	}

	/**
	 * Access the tag information.
	 * 
	 * @return a {@link TagBuilder}
	 * @see TagBuilder
	 */
	public TagBuilder withTag() {
		return new TagBuilder(info.getTag()).forComponent(info);
	}

	/**
	 * Accesses the renderer information and specifies the default renderer type
	 * (required if you want to register a separate renderer).
	 * 
	 * @param defaultRendererType
	 *            the DEFAULT_RENDERER_TYPE identifier
	 * @return a {@link RendererBuilder}
	 */
	public RendererBuilder withRenderer(String defaultRendererType) {
		info.setRendererType(defaultRendererType);
		return withRenderer();
	}

	/**
	 * Access the renderer information. In general,
	 * {@link #withRenderer(String)} should be called first, then subsequent
	 * accesses of the renderer information should use this method.
	 * 
	 * @return a {@link RendererBuilder}
	 */
	public RendererBuilder withRenderer() {
		return new RendererBuilder(info.getRenderer()).forComponent(info);
	}

	/**
	 * @return the {@link ComponentInfo} instance we've been building.
	 */
	public ComponentInfo getInfo() {
		return info;
	}

	public void andListeners(Class<? extends FacesListener>... listeners) {
		for (Class<? extends FacesListener> listener : listeners) {
			info.addListener(listener);
		}
	}
}
